#include <stdio.h>
#include "lcd.h"
#include "device.h"
#include "gpio.h"

#define lcd_res_high(gpio) sj_gpio_write(gpio,1)
#define lcd_res_low(gpio)  sj_gpio_write(gpio,0)

#define lcd_cs_high(gpio) sj_gpio_write(gpio,1)
#define lcd_cs_low(gpio)  sj_gpio_write(gpio,0)

#define lcd_scl_high(gpio) sj_gpio_write(gpio,1)
#define lcd_scl_low(gpio)  sj_gpio_write(gpio,0)

#define lcd_sda_high(gpio) sj_gpio_write(gpio,1)
#define lcd_sda_low(gpio)  sj_gpio_write(gpio,0)

#define lcd_cmd_high(gpio) sj_gpio_write(gpio,1) //RS
#define lcd_cmd_low(gpio)  sj_gpio_write(gpio,0)

#define lcd_dev_pin_delay(val) \
{\
    register int i,k;\
    for(i=0;i<val;i++)\
    for(k=0;k<200;k++);\
}

static  void lcd_write_one_byte(struct lcd_device_data *dev, sj_int8 data)
{
    sj_int8 i=0;
    struct gpio_pin *scl_pin=&(dev->scl);
    struct gpio_pin *sda_pin=&(dev->sda);

    for(i=0;i<8;i++)
    {
        if(data & 0x80)
        {
            lcd_sda_high(sda_pin);
        }
        else
        {
            lcd_sda_low(sda_pin);
        }

        lcd_scl_low(scl_pin);
        lcd_dev_pin_delay(2);
        lcd_scl_high(scl_pin);
        data<<=1;
    }
}

static void lcd_write_cmd(struct lcd_device_data *dev,sj_uint8 data)
{
    struct gpio_pin *cmd_pin=&(dev->cmd);
    struct gpio_pin *cs_pin=&(dev->cs);
    lcd_cs_low(cs_pin);
    lcd_cmd_low(cmd_pin);
    lcd_write_one_byte(dev,data);
    lcd_cs_high(cs_pin);
}

static void lcd_write_data(struct lcd_device_data *dev,sj_uint8 data)
{
    struct gpio_pin *cmd_pin=&(dev->cmd);
    struct gpio_pin *cs_pin=&(dev->cs);
    lcd_cs_low(cs_pin);
    lcd_cmd_high(cmd_pin);
    lcd_write_one_byte(dev,data);
    lcd_cs_high(cs_pin);
}
static void lcd_device_clear_page(struct lcd_device_data *dev,sj_int8 page,sj_int8 val)
{
    sj_int8 i,j;
    page = page|0xb0;
    for(i=page;i<page+1;i++)
    {
        lcd_write_cmd(dev,i);
        lcd_write_cmd(dev,0x10);
        lcd_write_cmd(dev,0x01);
        for(j=0;j<128;j++)
        {
            lcd_write_data(dev,val);
        }
    }
}
static void lcd_device_init(struct lcd_device_data *dev)
{
    struct gpio_pin *res_pin=&(dev->res);

    lcd_res_high(res_pin);
    lcd_dev_pin_delay(2);
    lcd_res_low(res_pin);
    lcd_dev_pin_delay(2);
    lcd_res_high(res_pin);
    lcd_dev_pin_delay(20);

    lcd_write_cmd(dev,0xa2);		//1/9 Bias
    lcd_write_cmd(dev,0xa6);		//
    lcd_write_cmd(dev,0xa0);		//ADC set (SEG)
    lcd_write_cmd(dev,0xc8);		//COM reves

    lcd_write_cmd(dev,0x24);		//有效值范围：0X20-0X27粗调 每升高一阶电压增加零点几伏
    lcd_write_cmd(dev,0x81);		//Electronic Volume Mode Set
    lcd_write_cmd(dev,0x2d);		//有效值范围：0X00-0X3F微调 每升高一阶电压增加零点零几伏
    lcd_write_cmd(dev,0x2c);
    lcd_dev_pin_delay(200);         //延时200mS
    lcd_write_cmd(dev,0x2e);
    lcd_dev_pin_delay(200);         //延时200mS
    lcd_write_cmd(dev,0x2f);		//The Power Control Set
    lcd_dev_pin_delay(200);         //延时200mS
    lcd_write_cmd(dev,0xaf);		//Lcd Disply ON
    //lcd_dev_pin_delay(200); //slj add
}

static sj_void lcd_flush(struct lcd_device_data *dev,sj_uint8 *k)
{
    sj_uint8 i,j;
    for(i=0xb4;i<0xb8;i++)
    {
        lcd_write_cmd(dev,i);
        lcd_write_cmd(dev,0x10);
        lcd_write_cmd(dev,0x01);
        for(j=0;j<128;j++)
        {
            lcd_write_data(dev,*k++);
        }
    }
    for(i=0xb0;i<0xb4;i++)
    {
        lcd_write_cmd(dev,i);
        lcd_write_cmd(dev,0x10);
        lcd_write_cmd(dev,0x01);
        for(j=0;j<128;j++)
        {
            lcd_write_data(dev,*k++);
        }
    }
}

static  sj_int8  lcd_device_open(struct file *file_node)
{
    sj_int8 ret =0;
    return ret;
}

static  sj_int8  lcd_device_write(struct file *file_node,sj_uint8 *data ,sj_int32 len,sj_loff_t * loff)
{
    struct lcd_device_data *self_data = (struct lcd_device_data *)(file_node->private_data);
    sj_int8 ret =-1;

    return ret;
}

static  sj_int8  lcd_device_read(struct file *file_node,sj_uint8 *data ,sj_int32 len,sj_loff_t * loff)
{
    return 0;
}

static  sj_int8  lcd_device_close(struct file *file_node)
{
    sj_int8 ret =0;
    struct lcd_device_data *self_data = (struct lcd_device_data *)(file_node->private_data);

    return  ret;
}

static  sj_int8  lcd_device_flush(struct file *file_node)
{
    sj_int8 ret =0;
    struct lcd_device_data *self_data = (struct lcd_device_data *)(file_node->private_data);
    struct lcd_framebuffer *fb = &self_data->fb;
    lcd_flush(self_data,fb->buffer);

    return ret;
}


static  sj_int8  lcd_device_seek(struct file *file_node,sj_uint8 index ,sj_uint8 cur_set)
{
    sj_int8 ret =0;

    return ret;
}

static sj_int8 lcd_device_probe(struct device *dev)
{
    sj_int8 ret =0;
    sj_int8 i=0;
    struct lcd_device_data *self_data =(struct lcd_device_data *)dev->private_data;
    struct lcd_framebuffer *fb = &self_data->fb;
    if(!self_data)
    {
        ret =-1;
        goto out;
    }
    struct rcc_apb_periph *rcc_apb= self_data->rcc_apb;

    struct gpio_pin *res_pin =&self_data->res;
    struct gpio_pin *cmd_pin=&(self_data->cmd);
    struct gpio_pin *cs_pin=&(self_data->cs);
    struct gpio_pin *scl_pin=&(self_data->scl);
    struct gpio_pin *sda_pin=&(self_data->sda);

    struct gpio_pin *pwr_pin=&(self_data->scl);
    struct gpio_pin *blight_pin=&(self_data->sda);

    while(rcc_apb->apb!=0)
    {
        sj_gpio_clock_config(rcc_apb);
        rcc_apb++;
    }

    sj_gpio_init(res_pin);
    sj_gpio_init(cmd_pin);
    sj_gpio_init(cs_pin);
    sj_gpio_init(scl_pin);
    sj_gpio_init(sda_pin);
    sj_gpio_init(pwr_pin);
    sj_gpio_init(blight_pin);



    lcd_res_high(res_pin);
    lcd_cmd_high(cmd_pin);
    lcd_cs_high(cs_pin);
    lcd_scl_high(scl_pin);
    lcd_sda_high(sda_pin);

    sj_gpio_write(pwr_pin,1);
    sj_gpio_write(blight_pin,1);

    lcd_device_init(self_data);
    lcd_dev_pin_delay(5); //slj add
    lcd_write_cmd(self_data,0xaf);//Lcd Disply ON slj add
    lcd_dev_pin_delay(5);
    lcd_dev_pin_delay(400);
    lcd_dev_pin_delay(400);

    for(i=0;i<fb->height;i++)
    {
        lcd_device_clear_page(self_data,i,0);
    }

    lcd_flush(self_data,fb->buffer);
out:
    return ret;
}

static sj_int8 lcd_device_ioctl(struct file *file_node,sj_int8 cmd ,sj_uint8 *data)
{
    sj_int8 ret =0;
    sj_int32 i;
    struct lcd_device_data *self_data =(struct lcd_device_data *)file_node->private_data;
    struct lcd_framebuffer *fb=(struct lcd_framebuffer *)data;

    switch(cmd)
    {
        case LCD_GET_BUF_INFO:
            fb->buffer =self_data->fb.buffer;
            fb->width  =self_data->fb.width;
            fb->height =self_data->fb.height;
            break;
        case LCD_CLEAN_BUFFER:
            memset(self_data->fb.buffer,0,self_data->fb.width*self_data->fb.height);
            break;
        case LCD_CLEAN_SCREEN_BUFFER:
            memset(self_data->fb.buffer,0,self_data->fb.width*self_data->fb.height);
            for(i=0; i<self_data->fb.height; i++)
            {
                sj_kprintf("clear lcd \r\n");
                lcd_device_clear_page(self_data,i,0);
            }
            break;
        default:
            break;
    }

    return ret;
}

static struct file_operations lcd_opt =
{
    lcd_device_open,
    lcd_device_close,
    lcd_device_flush,
    lcd_device_read,
    lcd_device_write,
    lcd_device_seek,
    NULL,
    lcd_device_ioctl,
};

static struct device_id_table lcd_id_table[]=
{
    {"12864"},
    {NULL}
};

static struct device_driver lcd_driver={
    "lcd",
    lcd_id_table,
    &lcd_opt,
    lcd_device_probe,
    NULL,
    NULL,
    NULL,
    NULL,
};


static DRIVER_MODULE_INIT(lcd_driver)

